<?php
Namespace Cyfy\Modules\Forms;
use Cyfy\Message;

/**
 * Form Class.
 *
 * Creates an object of a class, which can be sent both via AJAX and HTTP POST.
 *
 * @author Dandelion<dandelion8888@gmail.com>
 * @version 0.1
 */
abstract class Form extends \Cyfy\Translatable
{
    /**
     * Module this form belongs to.
     * @var string
     */    
    public $module;
    
    /**
     * Name of the form - class
     * @var string
     */
    public $name;
    
    /**
     * Where to redirect after submit.
     * @var string
     */
    public $action = '';
    
    /**
     * Whether vaules stays after submitting in the form cells.
     * @var bool
     */
    public $stay = false;
    
    /**
     * Wysiwyg form - this form will be 100% width of page.
     * @var bool
     */
    public $wysiwygForm = false;
    
    /**
     * JavaScript code, which will be run AFTER submit.
     * @var string
     */
    public $onSubmit = '';
    
    /**
     * Permission neccessary for view/submit form. Module name.
     * @var string
     */
    private $permissionModule = '';
    
    /**
     * Permission neccessary for view/submit form. Rule name.
     * @var string
     */
    private $permisionPermission = '';
    
    /**
     * Contents of the form - inputs, selects...
     * @var array
     */
    private $contents = array();
    
    /**
     * Contents of the form - buttons only
     * @var array
     */
    private $buttons = array();
    
    /**
     * Values of the inputs.
     * @var array
     */
    private $data = array();
    
    /**
     * Prefix of the form inputs when sending.
     * @var string
     */
    private $prefix = '';
    
    /**
     * Unique code of one instance of the form.
     * @var string
     */
    private $unique = '';
    
   
    /**
     * Creates a new instance of FORM.
     * Extracts module name and form name from classname.
     * Loads the structure - if not disabled.
     *
     * @param bool Don't call structure.
     */
    final function __construct($noStructure = false)
    {
        // class names
        $class = explode("\\", get_class($this));
        $this -> module = $class[2];
        $this -> name = $class[4];
        
        $fullname = $this -> module . "_" . $this -> name;
        
        // if uniq id exists
        if($_POST["formUniqueP"][$fullname])
        {
            $this -> unique = $_POST["formUniqueP"][$fullname];
        }
        // else generate new
        else
        {
            $this -> unique = uniqid();	
        }
        
        // load structure, if not disabled
        if(!$noStructure)
        {
            $this -> structure();
        }
    }
    
    /**
     * This method contains structure of a form.
     * Example:
     * <code>
     *   $this -> action = "user";
     * 
     *   $i = new Forms\TextBox("username", User::t(30));
     *   $i -> required = true;
     *   $i -> maxLength = 50;
     *   $this -> addItem($i);
     *
     *   $i = new Forms\TextBox("password", User::t(40));
     *   $i -> required = true;
     *   $i -> MakePassword();
     *   $i -> maxLength = 50;
     *   $this -> addItem($i);

     *   $b = new Forms\Button(User::t(110));
     *   $this -> addButton($b);
     * </code>
     */
    abstract function structure();
    
    /**
     * Dummy method.
     */
    function validate()
    {
        return true;
    }
    
    /*
     * Dummy method.
     */
    function submit()
    {
        return true;
    }
    
    
    /** 
     * The permission user must have to see and submit the form.
     * @param string Module name.
     * @param string Rule name.
     */
    final function permission($module, $permission){
        $this -> permissionModule = $module;
        $this -> permissionPermission = $permission;	
    }
    
    /**
     * Gets a value of one input.
     * @param string Input name.
     * @return mixed Element value.
     */
    final function get($name)
    {
        return $this -> data[$this -> prefix . $name];
    }
    
    /**
     * Gets all values in one array
     * @return mixed Element value.
     */
    final function getAll()
    {
        return $this -> data;
    }
    
    /**
     * Gets wanted values in one array
     * @return mixed Element value.
     */
    final function getSelection()
    {
        $wanted = func_get_args();
        $out = array();
        foreach($wanted as $code)
        {
            $out[$code] = $this -> get($code);
        }
        return $out;
    }
    
    /**
     * Fills the object with data from POST. It strips HTML tags and removes other harmful content.
     */
    final function loadPostData()
    {
        $this->data = array();
        foreach($this -> contents as $object)
        {
            if(method_exists($object, "LoadPostData")){
                $value = $object -> LoadPostData($this->prefix);
                $this->data = array_merge($this->data, $value);
            }
        }
    }
    
    /**
     * Fills the object with custom data.
     * @param array Associative array in format array("elementName" => "value", "element2name" => "another value");
     */
    final function setData($data)
    {
        foreach($this -> contents as $object)
        {
            /*if(method_exists($object, "setData")){
                $object -> setData($data);
                continue;
            }*/
            foreach($data as $name => $value)
            {
                if($object -> name == $name)
                {
                    $object -> value = $value;
                }
            }
        }
    }
    
     
    /**
     * Fills the object with data from constants of the module.
     * @param array Names of the constants and inputs of the module to be filled. Example: array("onPage", "hideTitle")
     */
    final function setDataFromConstants($names)
    {
        foreach($this -> contents as $object)
        {
            foreach($names as $name)
            {
                if($object -> name == $name)
                {
                    $class = "Cyfy\Modules\\" . $this-> module . "\Module";
                    $value = $class :: constant($name);
                    // if checkbox, check on same values
                    if(get_class($object) == "Cyfy\Modules\Forms\CheckBox"){
                        if($object -> value == $value)
                        {
                            $object -> checked = true;
                        }
                        else
                        {
                            $object -> checked = false;
                        }
                        continue;
                    }
                    // add new value
                    $object -> value = $value;
                }
            }
        }
    }
    
    /**
     * Fills elements SELECT with custom data.
     * @param string Element name.
     * @param array Associative array. Eg: array('key1'=>'value1', 'key2'=>'value2');
     */
    final function fillSelect($name, $data)
    {
        foreach($this -> contents as $object)
        {
            if($object -> name == $name)
            {
                $object -> data = $data;
                break;
            }
        }
    }
    
    /**
     * Check a checkbox, or radio. Compare with values - usually with radio.
     *
     * @param string Element name.
     * @param bool New check value. True by default.
     * @param string Checks it only if value of the objects equals this value.
     */
    final function check($name, $check = true, $value = "")
    {
        foreach($this -> contents as $object)
        {
            if($object -> name == $name)
            {
                // compare value as well?
                if($value){
                    if($object -> value != $value)
                    {
                        continue;
                    }
                }
                // check it
                $object -> checked = $check;
                break;
            }
        }
    }
    
    /**
     * Autovalidation checks if the inputs are correctly filled (length, ...).
     */
    final function autoValidate()
    {
        $rtrn = true;
        foreach($this -> contents as $object)
        {
            if(method_exists($object, "validate")){
                if(!$object -> Validate()){
                    $rtrn = false;
                }	
            }
        }
        return $rtrn;
    }
    
    /**
     * Autosubmit method. (file upload...)
     */
    
    final function autoSubmit()
    {
        foreach($this -> contents as $object)
        {
            if(method_exists($object, "submit")){
                $object -> Submit();
            }
        }
    }

    // CREATING FORMS

    /**
     * Adds an input into form container.
     *
     * @param resource Object, inherited from \Cyfy\Modules\Forms\Input
     */
    final function addItem($object)
    {
        $classname = get_class($object);
        switch($classname)
        {
            case "Cyfy\\Modules\\Forms\\FieldSet":
                $this -> contents = array_merge($this -> contents, $object -> getContents());
                break;
                
            case "Cyfy\\Modules\\Forms\\WysiwygEditor":
                $this -> wysiwygForm = true;
                break;
        }
        
        $this -> contents[$object->name] = $object;
    }
    
    
    /**
     * Adds a button into container.
     * @param resource Button element.
     */
    final function addButton($object)
    {
        $this -> buttons[] = $object;
    }
    
    /**
     * Shows the form.
     * @return string HTML output.
     */
    final function show()
    {
        // permission??
        if($this -> permissionModule && $this -> permissionPermission)
        {
            if(!\Cyfy\Cyfy :: permission($this -> permissionModule, $this -> permissionPermission))
            {
                return \Cyfy\Cyfy ::  noPermission(true);
            }
        }
    
        // insert JS, CSS
        \Cyfy\Modules\Forms\Module :: runonce();
        
        $fullname = $this -> module . "_" . $this -> name;
        $wysiwygForm = ($this -> wysiwygForm) ? " form-wysiwyg" : "";
        $output = "\n\n<form action=\"\" method=\"post\" name=\"{$fullname}\" onsubmit=\"formValidate(this, '{$fullname}'); return false;\" class=\"form_{$fullname}{$wysiwygForm}\">\n";
        // inputs
        foreach($this -> contents as $object)
        {
            if($object -> inFieldset == false)
            {
                $output .= $object -> __toString();
            }
        }
        
        // buttons
        $output .= "\n<div class=\"form-buttons\">\n";
        foreach($this -> buttons as $object)
        {
            $output .= $object -> __toString();
        }
        $output .= "</div>\n";
        
        // about required items
        if(\Cyfy\Modules\Forms\Module :: $requiredCaption){
            $output .= "\n <div class=\"form-text\"><p>" .  \Cyfy\Modules\Forms\Module :: REQUIRED_CHAR . " " .  \Cyfy\Modules\Forms\Module :: t(140) . "</p></div>\n";
        }
        
        // finishing
        $output .= "    <input type=\"hidden\" name=\"formAction\" value=\"{$this -> action}\" />\n";
        $output .= "    <input type=\"hidden\" name=\"formModule\" value=\"{$this -> module}\" />\n";
        $output .= "    <input type=\"hidden\" name=\"formName\" value=\"{$this -> name}\" />\n";
        $output .= "    <input type=\"hidden\" name=\"formUniqueP[{$fullname}]\" value=\"{$this -> unique}\" />\n";
    
        $output .= "</form>\n\n";
        
        return $output;
    }
    
    /**
     * Set temporary value for this form.
     * @param string Variable name.
     * @param string Value.
     */
    final function setTemp($name, $value)
    {
        $_SESSION["forms-temporary"][$this -> module . "_" . $this -> name][$this -> unique][$name] = $value;		
    }

    /**
     * Set temporary value for this form.
     * @param string Variable name.
     * @return string Value.
     */
    final function getTemp($name)
    {
        return $_SESSION["forms-temporary"][$this -> module . "_" . $this -> name][$this -> unique][$name];		
    }

}